/*
 * Copyright (C) 2018 Spreadtrum Communications Inc.
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
 * may be copied, distributed, and modified under those terms.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include "sprd_panel.h"
#include "sprd_dsi.h"
#include "dsi/mipi_dsi_api.h"
#include "sprd_dphy.h"
#include <i2c.h>

//#define CMD_MODE_ENABLE
static uint8_t init_data[] = {
	//Host I/F Setting
	0x15, 0x00, 0x00, 0x02, 0xB0, 0x00,
	0x15, 0x00, 0x00, 0x02, 0xCC, 0x04,
	0x15, 0x00, 0x00, 0x02, 0xE3, 0x01,

	//DSI control
	0x39, 0x00, 0x00, 0x03, 0xB6, 0x61, 0x2C,

	//display setting
	0x39, 0x00, 0x00, 0x07, 0xC0, 0x23, 0xB2, 0x22, 0x10, 0xC2, 0x7F,

	//display h-timing setting
	0x39, 0x00, 0x00, 0x17, 0xC1, 0x0B, 0x6F, 0x01, 0x80, 0x00, 0x00,
	0x00, 0x00, 0x7F, 0x03, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
	0x08, 0x08, 0x08, 0x08, 0x08,

	//stop recovery setting
	0x15, 0x00, 0x00, 0x02, 0xC3, 0x75,

	//display V-timing setting
	0x39, 0x00, 0x00, 0x1D, 0xC5, 0x06, 0x06, 0x40, 0x43, 0x00, 0x00,
	0x03, 0x01, 0x80, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x0E,

	//panel drive setting
	0x39, 0x00, 0x00, 0x03, 0xC6, 0x01, 0x02,

	//GOUT pin assignment
	0x39, 0x00, 0x00, 0x3B, 0xC8, 0x09, 0x13, 0x11, 0x00, 0x00, 0x26,
	0x24, 0x22, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x13, 0x11, 0x00, 0x00, 0x27,
	0x25, 0x23, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x01, 0x00,
	0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00,

	//Gamma setting
	0x39, 0x00, 0x00, 0x27, 0xCA, 0x1B, 0x29, 0x32, 0x41, 0x4C, 0x56,
	0x6C, 0x7D, 0x8B, 0x97, 0x4A, 0x56, 0x63, 0x76, 0x7F, 0x8C, 0x9B,
	0xA8, 0xBF, 0x1B, 0x29, 0x32, 0x41, 0x4C, 0x56, 0x6C, 0x7D, 0x8B,
	0x97, 0x4A, 0x56, 0x63, 0x76, 0x7F, 0x8C, 0x9B, 0xA8, 0xBF,

	//power setting for VCI1/2/3, DC2/3
	0x39, 0x00, 0x00, 0x07, 0xD0, 0x01, 0x4B, 0x41, 0x00, 0x35, 0x99,
	//power setting for VCL,VCLLVL
	0x15, 0x00, 0x00, 0x02, 0xD1, 0x03,
	//power setting for external booster
	0x39, 0x00, 0x00, 0x03, 0xD2, 0x8E, 0x0B,
	//register write control
	0x15, 0x00, 0x00, 0x02, 0xE5, 0x02,
	//VOCMDC setting
	0x39, 0x00, 0x00, 0x03, 0xD4, 0x00, 0xA0,
	//VPLVL/VNLVL setting
	0x39, 0x00, 0x00, 0x03, 0xD5, 0x24, 0x24,
	//TE ON
	0x15, 0x00, 0x00, 0x02, 0x35, 0x00,

	//CABC SETTING
	0x15, 0x00, 0x00, 0x02, 0x51, 0xFF,
	0x15, 0x00, 0x00, 0x02, 0x53, 0x2C,
	0x15, 0x00, 0x00, 0x02, 0x55, 0x00,

	//pause
	0x05, 0x78, 0x00, 0x01, 0x11,
	0x05, 0x32, 0x00, 0x01, 0x29,

	CMD_END
};

static int mipi_dsi_send_cmds(struct sprd_dsi *dsi, void *data)
{
	uint16_t len;
	struct dsi_cmd_desc *cmds = data;

	if ((cmds == NULL) || (dsi == NULL))
		return -1;

	for (; cmds->data_type != CMD_END;) {
		len = (cmds->wc_h << 8) | cmds->wc_l;
		mipi_dsi_dcs_write(dsi, cmds->payload, len);
		if (cmds->wait)
			msleep(cmds->wait);
		cmds = (struct dsi_cmd_desc *)(cmds->payload + len);
	}
	return 0;
}

static int r61350_init(void)
{
	struct sprd_dsi *dsi = &dsi_device;
	struct sprd_dphy *dphy = &dphy_device;

	mipi_dsi_lp_cmd_enable(dsi, true);
	mipi_dsi_send_cmds(dsi, init_data);
#ifdef CMD_MODE_ENABLE
	mipi_dsi_set_work_mode(dsi, SPRD_MIPI_MODE_CMD);
#else
	mipi_dsi_set_work_mode(dsi, SPRD_MIPI_MODE_VIDEO);
#endif
	mipi_dsi_state_reset(dsi);
	mipi_dphy_hs_clk_en(dphy, true);

	return 0;
}

static int r61350_readid(void)
{
	struct sprd_dsi *dsi = &dsi_device;
	uint8_t read_buf[5] = {0};
	uint8_t pwr_val;

	mipi_dsi_lp_cmd_enable(dsi, true);
	mipi_dsi_set_max_return_size(dsi, 5);
	mipi_dsi_dcs_read(dsi, 0xBF, &read_buf, 5);
	if((0x02 == read_buf[0]) && (0x3C == read_buf[1]) &&
	   (0x13 == read_buf[2]) && (0x50 == read_buf[3])) {
		pr_info("r61350 read id success!\n");
		return 0;
        }

	pr_err("r61350 read id failed!\n");
	return -1;
}

static int r61350_power(int on)
{
	if (on) {
		sprd_gpio_request(NULL, CONFIG_LCM_GPIO_RSTN);
		sprd_gpio_direction_output(NULL, CONFIG_LCM_GPIO_RSTN, 1);
		mdelay(5);
		sprd_gpio_direction_output(NULL, CONFIG_LCM_GPIO_RSTN, 0);
		mdelay(5);
		sprd_gpio_direction_output(NULL, CONFIG_LCM_GPIO_RSTN, 1);
		mdelay(20);
	} else {
		sprd_gpio_direction_output(NULL, CONFIG_LCM_GPIO_RSTN, 0);
		mdelay(5);
	}

	return 0;
}

static struct panel_ops r61350_ops = {
	.init = r61350_init,
	.read_id = r61350_readid,
	.power = r61350_power,
};

static struct panel_info r61350_info = {
	/* common parameters */
	.lcd_name = "lcd_r61350_truly_mipi_hd",
	.type = SPRD_PANEL_TYPE_MIPI,
	.bpp = 24,
//	.fps = 60,
	.width = 720,
	.height = 1280,

	/* DPI specific parameters */
	.pixel_clk = 64000000, /*Hz*/
	.rgb_timing = {
		.hfp = 31,
		.hbp = 31,
		.hsync = 20,
		.vfp = 16,
		.vbp = 32,
		.vsync = 2,
	},

	/* MIPI DSI specific parameters */
	.phy_freq = 500000,
	.lane_num = 4,
#ifdef CMD_MODE_ENABLE
	.work_mode = SPRD_MIPI_MODE_CMD,
#else
	.work_mode = SPRD_MIPI_MODE_VIDEO,
#endif
	.burst_mode = PANEL_VIDEO_BURST_MODE,
	.nc_clk_en = false,
};

struct panel_driver r61350_truly_driver = {
	.info = &r61350_info,
	.ops = &r61350_ops,
};
